<template>
  <div class="app-container">
    <!-- 搜索条件区域 -->
    <el-form :model="queryParams" ref="queryForm" :inline="true">
      <el-form-item label="菜单名称" prop="menuName">
        <el-input
            v-model="queryParams.menuName"
            placeholder="请输入菜单名称"
            clearable
            size="default"
            @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="菜单状态" clearable size="default" style="width: 200px">
          <el-option label="正常" value="1"/>
          <el-option label="停用" value="0"/>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" size="default" @click="handleQuery">搜 索</el-button>
        <el-button icon="Refresh" size="default" @click="resetQuery">重 置</el-button>
      </el-form-item>
    </el-form>

    <!-- 操作按钮区域 -->
    <el-row :gutter="10">
      <el-col :span="1.5">
        <el-button type="primary" plain icon="Plus" size="default" @click="add">新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="success" plain icon="Refresh" size="default" @click="refreshNode">刷新</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="info" plain icon="Menu" size="default" @click="toggleExpandAll">展开/折叠</el-button>
      </el-col>
    </el-row>

    <el-table
        :data="tableData"
        row-key="id"
        lazy
        :load="loadChildren"
        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
        @selection-change="handleSelectionChange"
        @row-click="handleRowClick"
        ref="multiTableRef"
        style="width: 100%;"
        height="100%"
        :cell-style="cellStyle"
        :header-cell-style="headerCellStyle"
    >
      <el-table-column type="selection" width="55" align="center"/>
      <el-table-column prop="id" label="ID" width="100" align="center"/>
      <el-table-column prop="menuName" label="菜单名称" min-width="100" align="center"/>
      <el-table-column prop="menuType" label="类型" width="80" align="center">
        <template #default="scope">
          <el-tag v-if="scope.row.menuType === 'M'" type="success">目录</el-tag>
          <el-tag v-else-if="scope.row.menuType === 'C'" type="primary">菜单</el-tag>
          <el-tag v-else-if="scope.row.menuType === 'F'" type="warning">按钮</el-tag>
          <el-tag v-else type="info">未知</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="icon" label="图标" width="70" align="center">
        <template #default="scope">
          <el-icon v-if="scope.row.icon">
            <component :is="scope.row.icon"/>
          </el-icon>
          <span v-else>-</span>
        </template>
      </el-table-column>
      <el-table-column prop="sortOrder" label="排序" width="80" align="center"/>
      <el-table-column prop="path" label="路由路径" min-width="120" align="center"/>
      <el-table-column prop="component" label="组件路径" min-width="120" align="center"/>
      <el-table-column prop="perms" label="权限标识" min-width="150" align="center"/>
      <el-table-column prop="status" label="状态" width="80" align="center">
        <template #default="scope">
          <el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
          <el-tag v-else type="danger">停用</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="isHidden" label="显示" width="80" align="center">
        <template #default="scope">
          <el-tag v-if="scope.row.isHidden === 0" type="success">显示</el-tag>
          <el-tag v-else type="info">隐藏</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="createTime" label="创建时间" min-width="160" align="center"/>
      <el-table-column label="操作" width="180" align="center">
        <template #default="scope">
          <div class="operation-links">
            <el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button link type="primary" icon="Plus" v-if="scope.row.menuType !== 'F'" @click="handleAdd(scope.row)">
              新增
            </el-button>
            <el-popconfirm title="确定删除吗？" @confirm="del(scope.row.id)">
              <template #reference>
                <el-button link type="danger" icon="Delete">删除</el-button>
              </template>
            </el-popconfirm>
          </div>
        </template>
      </el-table-column>
    </el-table>

    <!-- 表单对话框 -->
    <el-dialog v-model="dialogVisible" :title="form.id ? '编辑菜单' : '新增菜单'" width="580px" destroy-on-close>
      <el-form :model="form" :rules="rules" ref="form" label-width="120px">
        <el-form-item prop="menuType">
          <template #label>
            <span>
              <el-tooltip
                  content="目录(M)：用于分组管理菜单项，可包含子菜单；菜单(C)：可访问的功能页面；按钮(F)：页面上的功能按钮"
                  placement="top"
                  raw
              >
                <el-icon><QuestionFilled/></el-icon>
              </el-tooltip>
              菜单类型
            </span>
          </template>
          <el-radio-group v-model="form.menuType">
            <el-radio value="M">目录</el-radio>
            <el-radio value="C">菜单</el-radio>
            <el-radio value="F">按钮</el-radio>
          </el-radio-group>
        </el-form-item>

        <!-- 目录/菜单显示父菜单 -->
        <el-form-item label="上级菜单" prop="parentId" v-if="['M','C'].includes(form.menuType)">
          <el-tree-select
              v-model="form.parentId"
              lazy
              :load="loadNodes"
              ref="treeSelect"
              :props="{
               label: 'menuName',
               value: 'id',
               isLeaf: function(data) {
                 return !data.hasChildren;
               },
             }"
              node-key="id"
              check-strictly
              placeholder="请选择上级菜单"
              style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="菜单名称" prop="menuName">
          <el-input v-model="form.menuName" placeholder="请输入菜单名称，如：用户管理"/>
        </el-form-item>
        <!-- 目录/菜单显示图标 -->
        <el-form-item label="菜单图标" prop="icon" v-if="['M','C'].includes(form.menuType)">
          <template #label>
            <span>
              <el-tooltip content="菜单前显示的图标，目录和菜单必选" placement="top">
                <el-icon><QuestionFilled/></el-icon>
              </el-tooltip>
              菜单图标
            </span>
          </template>
          <div class="icon-select-box">
            <el-input v-model="form.icon" placeholder="点击右侧按钮选择图标" readonly>
              <template #prefix>
                <el-icon v-if="form.icon">
                  <component :is="form.icon"/>
                </el-icon>
              </template>
              <template #append>
                <el-button @click="iconDialogVisible = true">选择</el-button>
              </template>
            </el-input>
          </div>
        </el-form-item>
        <!-- 目录/菜单显示路由 -->
        <el-form-item prop="path" label="路由路径" v-if="form.menuType !== 'F'">
          <template #label>
            <span>
              <el-tooltip content="访问菜单的URL路径，以/开头，如/system/user" placement="top">
                <el-icon><QuestionFilled/></el-icon>
              </el-tooltip>
              路由路径
            </span>
          </template>
          <el-input v-model="form.path" placeholder="访问菜单的URL路径，以/开头，如：/system/user">
            <template #prefix>
              <el-icon>
                <Link/>
              </el-icon>
            </template>
          </el-input>
          <div class="el-form-item-help">
            <small class="text-muted">目录示例：/system，菜单示例：user（不要输入/前缀）</small>
          </div>
        </el-form-item>
        <!-- 仅菜单项显示组件路径 -->
        <el-form-item prop="component" label="组件路径" v-if="form.menuType === 'C'">
          <template #label>
            <span>
              <el-tooltip content="Vue组件的路径，如system/user/index" placement="top">
                <el-icon><QuestionFilled/></el-icon>
              </el-tooltip>
              组件路径
            </span>
          </template>
          <el-input v-model="form.component" placeholder="请输入组件路径，如：system/user/index">
            <template #prefix>
              <el-icon>
                <Document/>
              </el-icon>
            </template>
          </el-input>
          <div class="el-form-item-help">
            <small class="text-muted">对应src/views下的vue文件路径，不需要写.vue后缀</small>
          </div>
        </el-form-item>
        <!-- 目录显示重定向 -->
        <el-form-item prop="redirect" label="重定向地址" v-if="form.menuType === 'M'">
          <template #label>
            <span>
              <el-tooltip content="访问目录时自动跳转的地址，以/开头" placement="top">
                <el-icon><QuestionFilled/></el-icon>
              </el-tooltip>
              重定向地址
            </span>
          </template>
          <el-input v-model="form.redirect" placeholder="请输入重定向地址，如：/system/user">
            <template #prefix>
              <el-icon>
                <Connection/>
              </el-icon>
            </template>
          </el-input>
          <div class="el-form-item-help">
            <small class="text-muted">访问目录时自动跳转到的子菜单，如/system自动跳转到/system/user</small>
          </div>
        </el-form-item>
        <el-form-item label="显示顺序" prop="sortOrder">
          <el-input-number v-model="form.sortOrder" :min="0" controls-position="right"/>
        </el-form-item>
        <!-- 菜单/按钮显示权限标识 -->
        <el-form-item prop="perms" label="权限标识" v-if="['C','F'].includes(form.menuType)">
          <el-input v-model="form.perms" placeholder="请输入权限标识，如：system:user:list">
            <template #prefix>
              <el-icon>
                <Key/>
              </el-icon>
            </template>
          </el-input>
          <div class="el-form-item-help">
            <small class="text-muted">用于控制后端接口权限，格式为模块:菜单:操作，如system:user:add</small>
          </div>
        </el-form-item>
        <el-form-item label="菜单状态" prop="status">
          <template #label>
            <span>
              <el-tooltip content="停用后该菜单将不会显示在左侧菜单栏" placement="top">
                <el-icon><QuestionFilled/></el-icon>
              </el-tooltip>
              菜单状态
            </span>
          </template>
          <el-radio-group v-model="form.status">
            <el-radio :value="1">正常</el-radio>
            <el-radio :value="0">停用</el-radio>
          </el-radio-group>
        </el-form-item>
        <!-- 目录/菜单显示更多选项 -->
        <el-form-item label="更多选项" v-if="['M','C'].includes(form.menuType)">
          <el-row :gutter="20">
            <el-col :span="8">
              <el-tooltip content="勾选后该菜单不会显示在左侧菜单栏中，但可以通过URL访问" placement="top">
                <el-checkbox v-model="form.isHidden" label="隐藏菜单" border/>
              </el-tooltip>
            </el-col>
            <el-col :span="8">
              <el-tooltip content="勾选后该页面组件会被缓存，避免重复渲染" placement="top">
                <el-checkbox v-model="form.isCache" label="开启缓存" border/>
              </el-tooltip>
            </el-col>
            <el-col :span="8">
              <el-tooltip content="勾选后该路径会以新窗口打开" placement="top">
                <el-checkbox v-model="form.isFrame" label="外部链接" border/>
              </el-tooltip>
            </el-col>
          </el-row>
        </el-form-item>
        <el-form-item label="备注" prop="remark" v-if="['M','C'].includes(form.menuType)">
          <el-input v-model="form.remark" type="textarea" placeholder="请输入备注信息（选填）"/>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="save">确 定</el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 图标选择器对话框 -->
    <el-dialog v-model="iconDialogVisible" title="选择图标" width="700px" destroy-on-close>
      <div class="icon-filter-bar">
        <el-input
            v-model="iconFilter"
            placeholder="输入图标名称过滤"
            prefix-icon="Search"
            clearable
        />
      </div>
      <div class="icon-grid">
        <div
            v-for="icon in filteredIcons"
            :key="icon"
            class="icon-item"
            @click="selectIcon(icon)"
            :class="{ 'active': form.icon === icon }"
        >
          <el-icon :size="24">
            <component :is="icon"/>
          </el-icon>
          <div class="icon-name">{{ icon }}</div>
        </div>
      </div>
      <template #footer>
        <el-button @click="iconDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="iconDialogVisible = false">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script>
// @ is an alias to /src
// 导入所有图标
import * as Icons from "@element-plus/icons-vue";
import {
  QuestionFilled,
  Search,
  Link,
  Document,
  Connection,
  Key
} from "@element-plus/icons-vue";
import {
  addMenu,
  delMenu,
  getInfo,
  listChildNode,
  listRootMenu,
  updateMenu
} from "@/api/menu";

export default {
  name: 'Menu',
  components: {
    QuestionFilled,
    Search,
    Link,
    Document,
    Connection,
    Key
  },
  data() {
    return {
      // 表格相关
      table: null,
      selectionRows: [],
      expandRowKeys: [1, 2], // 存储需要展开的行id
      isExpanded: false, // 展开状态标志
      selectedIds: [], // 记录选中的ID
      id: 0, // 记录当前操作的id
      tableData: [],

      // 查询参数
      queryParams: {
        menuName: '',
        status: ''
      },

      // 表单相关
      dialogVisible: false,
      serverValidErrors: {},
      form: {
        id: null,
        parentId: null,
        menuName: '',
        menuType: 'M',
        path: '',
        component: '',
        perms: '',
        icon: '',
        sortOrder: 0,
        status: 1,
        isHidden: 0,
        isCache: 0,
        isFrame: 0,
        redirect: '',
        remark: ''
      },

      // 图标选择器相关
      iconDialogVisible: false,
      iconFilter: '',
      icons: Object.keys(Icons),

      // 定义添加表单校验规则
      rules: {
        menuName: [{required: true, message: '请输入菜单名称', trigger: 'blur'}],
        menuType: [{required: true, message: '请选择菜单类型', trigger: 'change'}],
        path: [
          {required: true, message: '请输入路由路径', trigger: 'blur'},
          {pattern: /^\/\w+/i, message: '路由必须以/开头', trigger: 'blur'}
        ],
        component: [{required: true, message: '请输入组件路径', trigger: 'blur'}],
        perms: [
          {pattern: /^[a-z:]+:[a-z]+$/, message: '格式如system:user:list', trigger: 'blur'}
        ],
        sortOrder: [{type: 'number', required: true, message: '必须为数字', trigger: 'blur'}],
        status: [{required: true, message: '请选择状态', trigger: 'change'}]
      }
    }
  },
  computed: {
    // 过滤图标
    filteredIcons() {
      if (!this.iconFilter) return this.icons;
      return this.icons.filter(icon => icon.toLowerCase().includes(this.iconFilter.toLowerCase()));
    }
  },
  methods: {
    // 表格单元格样式
    cellStyle() {
      return {
        padding: '5px 0',
      };
    },
    // 表头样式
    headerCellStyle() {
      return {
        padding: '5px 0',
      };
    },
    // 搜索按钮操作
    handleQuery() {
      this.getList();
    },
    // 重置按钮操作
    resetQuery() {
      this.queryParams = {
        menuName: '',
        status: ''
      };
      this.getList();
    },
    // 新增子菜单
    handleAdd(row) {
      this.dialogVisible = true;
      this.form = {
        menuType: 'C',
        status: 1,
        sortOrder: 0,
        isHidden: 0,
        isCache: 0,
        isFrame: 0,
        parentId: row ? row.id : null
      };
      if (this.$refs['form']) {
        this.$refs['form'].resetFields();
      }
      this.serverValidErrors = {};
    },
    refreshNode() {
      // 检查是否选中节点
      if (this.selectionRows.length !== 1) {
        this.$message.warning('请先选择一个节点');
        return;
      }

      const row = this.selectionRows[0];

      // 先折叠节点
      this.table.toggleRowExpansion(row, false);

      // 等待DOM更新
      this.$nextTick(() => {
        // 手动触发加载
        this.table.load(row, null, (children) => {
          // 如果加载成功且有子节点
          if (children && children.length > 0) {
            // 展开节点
            this.table.toggleRowExpansion(row, true);
            this.$message.success(`成功加载 ${children.length} 个子节点`);
          } else {
            this.$message.info('该节点没有子节点');
          }
        });
      });
    },
    loadChildren(row, treeNode, resolve) {
      listChildNode(row.id).then(res => {
        if (res.code != '200') {
          this.$message.error(res.msg);
          return;
        }
        resolve(res.data);
      })
    },
    loadNodes(node, resolve) {
      if (node.level === 0) {
        listRootMenu().then(res => {
          resolve(res.data);
        })
      } else {
        listChildNode(node.data.id).then(res => {
          resolve(res.data);
        })
      }
    },
    selectIcon(iconName) {
      this.form.icon = iconName;
      this.iconDialogVisible = false;
    },
    add() {
      this.dialogVisible = true;//显示对话框
      this.form = {//初始化表单
        menuType: 'M',
        status: 1,
        sortOrder: 0,
        isHidden: 0,
        isCache: 0,
        isFrame: 0
      };
      // 清空上次校验的信息
      // 确保表单已经加载完成
      if (this.$refs['form']) {
        this.$refs['form'].resetFields();
      }
      //清空上次后端校验的信息
      this.serverValidErrors = {};
    },
    save() {
      this.$refs.form.validate(valid => {
        if (!valid) return;

        // 转换布尔值为数字，确保与数据库字段类型一致
        const formData = {...this.form};

        // 确保数值类型正确
        formData.isHidden = formData.isHidden ? 1 : 0;
        formData.isCache = formData.isCache ? 1 : 0;
        formData.isFrame = formData.isFrame ? 1 : 0;
        formData.status = parseInt(formData.status);
        formData.sortOrder = parseInt(formData.sortOrder);

        // 确保parentId为undefined或空时，传递为null
        if (formData.parentId === undefined || formData.parentId === '' || formData.parentId === 0) {
          formData.parentId = null;
        }

        // 修改和添加
        if (formData.id) {
          updateMenu(formData).then(res => {
            if (res.code == 200) {
              this.$message({
                type: 'success',
                message: '修改成功'
              });
              //刷新页面
              this.dialogVisible = false;
              this.getList();
            } else {
              this.$message({
                type: 'error',
                message: res.msg || '修改失败'
              });
            }
          })
        } else {
          addMenu(formData).then(res => {
            if (res.code == 200) {
              this.$message({
                type: 'success',
                message: '新增成功'
              });
              //刷新页面
              this.dialogVisible = false;
              this.getList();
            } else {
              this.$message({
                type: 'error',
                message: res.msg || '新增失败'
              });
              if (res.data && res.data.errorFields) {
                this.serverValidErrors = res.data.errorFields;
              }
            }
          })
        }
      });
    },
    getList() {
      listRootMenu().then(res => {
        this.tableData = res.data;
        this.table = this.$refs.multiTableRef;
      })
    },
    reset() {
      this.queryParams = {
        menuName: '',
        status: ''
      };
      this.getList();
    },
    handleEdit(row) {
      // 确保表单已经加载完成
      if (this.$refs['form']) {
        this.$refs['form'].resetFields();
      }
      this.dialogVisible = true;
      getInfo(row.id).then(res => {
        if (res.code === 200) {
          // 转换数字为布尔值，方便表单使用
          const formData = {...res.data};
          // 处理字段转换
          formData.isHidden = formData.isHidden === 1;
          formData.isCache = formData.isCache === 1;
          formData.isFrame = formData.isFrame === 1;
          this.form = formData;
        } else {
          this.$message.error(res.msg || '获取菜单信息失败');
        }
      })
    },
    del(id) {
      delMenu(id).then(res => {
        if (res.code == 200) {
          this.$message({
            type: 'success',
            message: '删除成功'
          });
          this.getList();
        } else {
          this.$message({
            type: 'error',
            message: res.msg || '删除失败'
          });
        }
      })
    },
    // 处理选中事件
    handleSelectionChange(rows) {
      this.selectedIds = rows.map(row => row.id);
      this.id = this.selectedIds[0];
      this.selectionRows = rows;
    },
    handleRowClick(row) {
      // 判断是否有子节点
      if (!row.hasChildren) {
        return;
      }
      this.isExpanded = !this.isExpanded;
      this.table.toggleRowExpansion(row, !this.isExpanded);
    },
    // 展开/折叠所有节点
    toggleExpandAll() {
      this.isExpanded = !this.isExpanded;
      if (this.isExpanded) {
        // 展开所有
        this.expandAll(this.tableData);
      } else {
        // 折叠所有
        this.collapseAll(this.tableData);
      }
    },
    // 递归展开所有节点
    expandAll(data) {
      data.forEach(row => {
        if (row.hasChildren) {
          this.table.toggleRowExpansion(row, true);
          if (row.children && row.children.length > 0) {
            this.expandAll(row.children);
          }
        }
      });
    },
    // 递归折叠所有节点
    collapseAll(data) {
      data.forEach(row => {
        if (row.hasChildren) {
          this.table.toggleRowExpansion(row, false);
          if (row.children && row.children.length > 0) {
            this.collapseAll(row.children);
          }
        }
      });
    },
  },
  created() {
    this.getList();

    // 添加ResizeObserver错误捕获
    window.addEventListener('error', (e) => {
      if (e && e.message && (
          e.message === 'ResizeObserver loop limit exceeded' ||
          e.message.includes('ResizeObserver loop completed with undelivered notifications')
      )) {
        const resizeObserverErr = e;
        resizeObserverErr.stopImmediatePropagation();
      }
    }, true);
  }
}
</script>

<style scoped>
/* 图标选择器样式 */
.icon-filter-bar {
  margin-bottom: 15px;
}

.icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  max-height: 350px;
  overflow-y: auto;
}

.icon-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 12px 8px;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
}

.icon-item:hover {
  background-color: #ecf5ff;
  border-color: #409eff;
}

.icon-item.active {
  background-color: #409eff;
  border-color: #409eff;
  color: #fff;
}

.icon-name {
  margin-top: 8px;
  font-size: 12px;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
  width: 100%;
  text-align: center;
}

/* 图标选择输入框 */
.icon-select-box {
  display: flex;
  align-items: center;
  width: 100%;
}
</style>

